﻿namespace SharpF2.Components
{
	using System;
	using System.Collections.ObjectModel;
	using System.ComponentModel;

	/// <summary>
	/// Define a sortable observable collection
	/// </summary>
	/// <typeparam name="T">Type of template</typeparam>
	public class SortableObservableCollection<T> : ObservableCollection<T>, INotifyInnerDataChanged
	{
		#region Events

		public event EventHandler InnerDataChanged;

		#endregion

		#region Private Attributes

		private Comparison<T> _compare;
		private ListSortDirection _direction;

		#endregion

		#region Public Members

		public void OnInnerDataChanged()
		{
			if (null != InnerDataChanged)
				InnerDataChanged(this, EventArgs.Empty);
		}

		/// <summary>
		/// Sort the collection using a already used comparer.
		/// </summary>
		public void Sort()
		{
			if (null != _compare)
				MergeSort(0, Count - 1);
		}

		/// <summary>
		/// Sort the collection using a new comparer and a new direction.
		/// </summary>
		/// <param name="compare">The comparer defining the order.</param>
		/// <param name="direction">The direction of the comparaison</param>
		public void Sort(Comparison<T> compare, ListSortDirection direction)
		{
			_compare = compare;
			_direction = direction;
			MergeSort(0, Count - 1);
		}

		#endregion

		#region Private Members

		private Int32 Compare(T x, T y)
		{
			return (_direction.Equals(ListSortDirection.Ascending))
			       	? _compare(x, y)
			       	:
			       		-_compare(x, y);
		}

		/// <summary>
		/// Sort and Merge using a comparer.
		/// </summary>
		/// <param name="left">The left index part.</param>
		/// <param name="mid">The mid index part.</param>
		/// <param name="right">The right index part.</param>
		private void Merge(int left, int mid, int right)
		{
			var a = this;
			var b = new T[Count];
			int i;

			var leftEnd = mid - 1;
			var position = left;
			var numElements = right - left + 1;

			while ((left <= leftEnd) && (mid <= right))
			{
				var compare = Compare(a[left], a[mid]);
				if (compare <= 0)
				{
					b[position] = a[left];
					position = position + 1;
					left = left + 1;
				}
				else
				{
					b[position] = a[mid];
					position = position + 1;
					mid = mid + 1;
				}
			}

			while (left <= leftEnd)
			{
				b[position] = a[left];
				left = left + 1;
				position = position + 1;
			}

			while (mid <= right)
			{
				b[position] = a[mid];
				mid = mid + 1;
				position = position + 1;
			}

			for (i = 0; i < numElements; i++)
			{
				a[right] = b[right];
				right = right - 1;
			}
		}

		/// <summary>
		/// Merge sort a part of the collection using a comparer.
		/// </summary>
		/// <param name="left">The left index part.</param>
		/// <param name="right">The right index part.</param>
		private void MergeSort(int left, int right)
		{
			if (right > left)
			{
				var mid = (right + left) / 2;
				MergeSort(left, mid);
				MergeSort(mid + 1, right);

				Merge(left, mid + 1, right);
			}
		}

		#endregion
	}
}